#include "GraphicsHelper.hpp"
#include "GeometryHelper.hpp"
#include "../Resource/Mesh/SimpleMesh.hpp"
#include "../Resource/Shader/Shader.hpp"
#include "OpenGLTools.hpp"
#include "ColorDefine.hpp"

namespace zzz{
void GraphicsHelper::DrawPlane(const Vector3d &point, const Vector3d &normal, double size) const
{
  Vector3d one=normal.RandomPerpendicularUnitVec(), another=normal.Cross(one);
  one*=size/2;another*=size/2;
  glBegin(GL_QUADS);
    glVertex3dv((point-one-another).Data());
    glVertex3dv((point-one+another).Data());
    glVertex3dv((point+one+another).Data());
    glVertex3dv((point+one-another).Data());
  glEnd();
}

void GraphicsHelper::DrawGrid(const zzz::Vector<3,double> &point, const zzz::Vector<3,double> &normal, double size, double interval) const
{
  Vector3d one=normal.RandomPerpendicularUnitVec(), another=normal.Cross(one);
  int num=size/interval/2.0f;
  glBegin(GL_LINES);
  for (int i=-num; i<=num; i++)
  {
    glVertex3dv((point+one*interval*i+another*interval*num).Data());
    glVertex3dv((point+one*interval*i-another*interval*num).Data());
    glVertex3dv((point+another*interval*i+one*interval*num).Data());
    glVertex3dv((point+another*interval*i-one*interval*num).Data());
  }
  glEnd();
}



const double X=0.525731112119133606;
const double Z=0.850650808352039932;
GLdouble vdata[12][3]={
{-X,0.0,Z},{X,0.0,Z},{-X,0.0,-Z},{X,0.0,-Z},
{0.0,Z,X},{0.0,Z,-X},{0.0,-Z,X},{0.0,-Z,-X},
{Z,X,0.0},{-Z,X,0.0},{Z,-X,0.0},{-Z,-X,0.0},
};
GLuint tindices[20][3]={
{1,4,0},{4,9,0},{4,5,9},{8,5,4,},{1,8,4},
{1,10,8},{10,3,8},{8,3,5},{3,2,5},{3,7,2},
{3,10,7},{10,6,7},{6,11,7},{6,0,11},{6,1,0},
{10,1,6},{11,0,9},{2,11,9},{5,2,9},{11,2,7},
};

void GraphicsHelper::DrawSphere(const double r, const int levels) const
{
  SimpleMesh mesh;
  GeometryHelper::CreateSphere(mesh, levels);
  glPushMatrix();
  glScaled(r,r,r);
  glBegin(GL_TRIANGLES);
  for(STLVector<Vector3i>::const_iterator vi = mesh.faces_.begin(); vi != mesh.faces_.end(); vi ++)
  {
    const Vector3i &f = *vi;
    glNormal3fv(mesh.vertices_[f[0]].Data());
    glVertex3fv(mesh.vertices_[f[0]].Data());
    glNormal3fv(mesh.vertices_[f[1]].Data());
    glVertex3fv(mesh.vertices_[f[1]].Data());
    glNormal3fv(mesh.vertices_[f[2]].Data());
    glVertex3fv(mesh.vertices_[f[2]].Data());
  }
  glEnd();
  glPopMatrix();

}

void GraphicsHelper::DrawAABB(const AABB<3,float> &aabb, const Colorf& color, float width) const
{
  GLLineWidth::Set(width);
  Vector3f v[8]={\
    aabb.Min(),  Vector3f(aabb.Max()[0],aabb.Min()[1],aabb.Min()[2]),\
    Vector3f(aabb.Max()[0],aabb.Min()[1],aabb.Max()[2]), Vector3f(aabb.Min()[0],aabb.Min()[1],aabb.Max()[2]),\
    aabb.Max(), Vector3f(aabb.Min()[0],aabb.Max()[1],aabb.Max()[2]),\
    Vector3f(aabb.Min()[0],aabb.Max()[1],aabb.Min()[2]), Vector3f(aabb.Max()[0],aabb.Max()[1],aabb.Min()[2])};
  color.ApplyGL();
  ZRM->Get<Shader*>("ColorShader")->Begin();
  glBegin(GL_LINES);
    glVertex3fv(v[0].Data());
    glVertex3fv(v[1].Data());

    glVertex3fv(v[1].Data());
    glVertex3fv(v[2].Data());

    glVertex3fv(v[2].Data());
    glVertex3fv(v[3].Data());

    glVertex3fv(v[3].Data());
    glVertex3fv(v[0].Data());

    glVertex3fv(v[4].Data());
    glVertex3fv(v[5].Data());

    glVertex3fv(v[5].Data());
    glVertex3fv(v[6].Data());

    glVertex3fv(v[6].Data());
    glVertex3fv(v[7].Data());

    glVertex3fv(v[7].Data());
    glVertex3fv(v[4].Data());

    glVertex3fv(v[0].Data());
    glVertex3fv(v[6].Data());

    glVertex3fv(v[1].Data());
    glVertex3fv(v[7].Data());

    glVertex3fv(v[2].Data());
    glVertex3fv(v[4].Data());

    glVertex3fv(v[3].Data());
    glVertex3fv(v[5].Data());
  glEnd();
  Shader::End();

  GLLineWidth::Restore();
}

void GraphicsHelper::DrawCoord(const Vector3d &point, double length/*=10*/, int linewidth/*=4*/)
{
  GLLineWidth::Set(linewidth);
  ZRM->Get<Shader*>("ColorShader")->Begin();
  glBegin(GL_LINES);
    ColorDefine::red.ApplyGL();
    glVertex3dv(point.Data());
    glVertex3d(point[0]+length, point[1], point[2]);
    ColorDefine::green.ApplyGL();
    glVertex3dv(point.Data());
    glVertex3d(point[0], point[1]+length, point[2]);
    ColorDefine::blue.ApplyGL();
    glVertex3dv(point.Data());
    glVertex3d(point[0], point[1], point[2]+length);
  glEnd();
  Shader::End();
  GLLineWidth::Restore();
}

}